<template>
  <section class="factory-main">
    <div class="three" ref="container">
      <div class="css3d" ref="css3d"></div>
    </div>
    <div class="tools">
      <input type="button" value="视角还原" @click="restoreScene" />
      <input
        type="button"
        :value="isFire ? '关闭火情' : '发生火情'"
        @click="fireControls"
      />
    </div>
  </section>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import Hthree from '../utils/Hthree';
import TWEEN from "@tweenjs/tween.js";
import { useSky } from '../utils/useSky';
import { CSS3DSprite } from 'three/examples/jsm/renderers/CSS3DRenderer';
import {
  Vector3, Raycaster, Vector2, DirectionalLight, AmbientLight, SpotLight,
  Group, SpotLightHelper, sRGBEncoding, Color, TextureLoader, SpriteMaterial,
  AdditiveBlending, Sprite, Object3D, AnimationMixer, Clock, CatmullRomCurve3,
  TubeGeometry, RepeatWrapping, MeshPhongMaterial, DoubleSide, Mesh,
} from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
let T = null;
let spotLight = null;
let cameraPos = null;
let cameraRotation = null;
let krq = new Object3D();
let mixer = null;
const container = ref();
const css3d = ref();
const modelScale = 8;
const isFire = ref(false);  // 是否发生火灾
const gltfLoaderer = new GLTFLoader().setPath('../../../model');
const clock = new Clock();
const lineTexture = new TextureLoader().load('/images/fire/lightTiao.png');
const loopTime = 50 * 800; // loopTime: 循环一圈的时间
lineTexture.wrapS = RepeatWrapping;
lineTexture.wrapT = RepeatWrapping;
lineTexture.repeat.set(20, 1);
lineTexture.needsUpdate = true;
let curve = null;
let worker = null;


onMounted(() => {
  init();
  render();
})

const init = () => {
  T = new Hthree({
    container: container.value,
    css3dContainer: css3d.value,
    controls: {
      open: true,
      enableDamping: true,
      enableRotate: true,
      enablePan: true,
      enableZoom: true
    },
    cameraConfig: { fov: 60, far: 3000 }
  });
  T.scene.rotateY(-Math.PI / 4);
  T.controls.maxPolarAngle = Math.PI; // 垂直旋转最大角度
  T.controls.minPolarAngle = 0; // 垂直旋转最小角度
  // T.controls.maxAzimuthAngle = Math.PI / 10; // 水平旋转最大角度
  // T.controls.minAzimuthAngle = -Math.PI / 10;   // 水平旋转最小角度
  T.controls.maxDistance = 388;
  T.controls.minDistance = 10;
  cameraPos = [...T.camera.position];
  cameraRotation = [T.camera.rotation.x, T.camera.rotation.y, T.camera.rotation.z];
  const sky = useSky(T.scene, T.camera, T.renderer);
  T.scene.add(sky);
  initLight();
  addFactory();
  container.value.addEventListener('click', rayCasterSel, false);
  addFire({ pos: new Vector3(10, -10, 20), scale: 3 });
  addWorders();
};
const render = () => {
  T.renderer.render(T.scene, T.camera);
  requestAnimationFrame(render);
  T.css3dRenderer.render(T.scene, T.camera);
  TWEEN.update();
  const delta = clock.getDelta();
  mixer ? mixer.update(delta) : '';
  lineTexture ? lineTexture.offset.x -= 0.10 : '';
  isFire.value && curve ? droneAni() : '';
};
const initLight = () => {
  const group = new Group();
  // 平行光
  const directionalLight = new DirectionalLight(0xffffff, 1);
  directionalLight.position.set(-250, 250, 100);
  group.add(directionalLight);
  //环境光
  const ambient = new AmbientLight(0xffffff, 0.8);
  group.add(ambient);
  spotLight = new SpotLight(0xffffff, 3);
  spotLight.position.set(10, 50, 20);
  // spotLight.castShadow = true;
  spotLight.shadow.mapSize.width = 1024;
  spotLight.shadow.mapSize.height = 1024;
  spotLight.shadow.camera.near = 100;
  spotLight.shadow.camera.far = 1000;
  spotLight.shadow.camera.fov = 30;
  // const spotLightHelper = new SpotLightHelper(spotLight);
  group.add(spotLight);
  T.scene.add(group);
};
const gltfLoader = url => {
  return new Promise((resolve, reject) => {
    gltfLoaderer.load(url, resolve, xhr => {// onProgress回调
      let percentage = Math.floor(xhr.loaded / xhr.total * 100);
      console.log(percentage + '% loaded');
    }, reject)
  })
};
const addFactory = async () => {
  const { scene } = await gltfLoader('/glb/factory2.glb');
  scene.scale.multiplyScalar(modelScale);
  spotLight.target = scene;
  scene.traverse(mesh => {
    if (mesh.isMesh) {
      mesh.material.emissiveMap = mesh.material.map;
      mesh.geometry.computeBoundingBox();
      let pos = new Vector3();
      pos.addVectors(mesh.geometry.boundingBox.min, mesh.geometry.boundingBox.max);
      pos.multiplyScalar(0.03);
      pos.applyMatrix4(mesh.matrixWorld)
      mesh.pos = new Vector3(pos.x, pos.y + 3, pos.z);
      if (
        mesh.name === "tank_1_tank_blue_m_0"
      ) {
        // mesh.material.color = new Color(0x000222)
        console.log(mesh.pos, 'lll')
      }
    }
  })
  T.renderer.outputEncoding = sRGBEncoding;
  T.scene.add(scene);
};
const addFire = (config = { pos, scale }) => {
  const textLoader = new TextureLoader();
  const texture = textLoader.load('/images/fire/qxcar.png');
  const mat = new SpriteMaterial({
    map: texture,
    blending: AdditiveBlending,
    color: 0xff2200
  });
  //循环1000  添加粒子
  for (var i = 0; i < 2000; i++) {
    var particle = new Sprite(mat);
    initParticle(particle, i, config.scale);
    krq.add(particle);
    krq.name = "particles_flame";
  }
  krq.position.copy(config.pos);
  krq.visible = false;
  T.scene.add(krq);
};
const initParticle = (particle, delay, scale) => {
  particle.position.set(5, Math.random() + 5, 0);
  particle.scale.x = particle.scale.y = Math.random() * scale;
  //下面是一系列的动画
  var xx = Math.random() * 20 - 5;
  var yy = Math.cos((Math.PI / 100) * xx) * 30;
  //位移
  new TWEEN.Tween(particle.position)
    .delay(delay)
    .to({
      x: xx,
      y: yy,
      z: Math.random() * 10 - 5
    }, 2000)
    .onComplete(function () {
      initParticle(particle, delay, scale);
    })
    .start();
  // 大小
  new TWEEN.Tween(particle.scale)
    .delay(delay)
    .to({
      x: 0.01,
      y: 0.01
    }, 1000)
    .start();
};
const fireControls = () => {
  isFire.value = !isFire.value;
  const fire = T.scene.getObjectByName('particles_flame');
  let marker = document.querySelector('.f-marker');
  fire.visible = isFire.value;
  if (isFire.value) {
    new TWEEN.Tween(T.camera.position).to({
      x: fire.position.x - 10,
      y: fire.position.y + 15,
      z: fire.position.z + 15
    }, 3000).easing(TWEEN.Easing.Cubic.InOut).onUpdate(() => {
      T.camera.lookAt(T.camera.position)
    }).delay(1500).onComplete(() => {
      if (marker) {
        marker.hidden = false;
      } else {
        marker = addMarker({
          text: '报警:因工作流程操作不当，发生火情',
          pos: new Vector3(fire.position.x, fire.position.y + 20, fire.position.z),
          m_class: 'fire-marker f-marker'
        })
        T.scene.add(marker);
      }
      addEscapeLine();  // 逃生线路
    }).start();
  } else {
    restoreScene();
    marker.hidden = true;
  }
};
const addWorders = async () => {
  const res = await gltfLoader('/glb/Soldier.glb');
  const { animations, scene } = res;
  worker = scene;
  T.scene.add(scene);
  // 开启模型本身的动画
  mixer = new AnimationMixer(scene);
  scene.name = 'worker';
  const action = mixer.clipAction(animations[1]);
  action.play();
  console.log(animations, 'person', scene);
  // scene.position.copy(new Vector3(10.709318501258421, 0.000003530609109464264, 19.48467859439551));
};
const addMarker = con => {
  const config = Object.assign({ pos: new Vector3(0, 0, 3), scale: 0.05, text: 'MARKER', b_class: 'marker-body', m_class: 'fire-marker' }, con);
  const marker_body = createDOM('div', { class: config.b_class });
  const marker_dom = createDOM('div', { class: config.m_class });
  marker_dom.innerText = config?.text;
  marker_body.appendChild(marker_dom);
  const marker = new CSS3DSprite(marker_body);
  marker.position.copy(config?.pos);
  marker.scale.multiplyScalar(0.05);
  marker.rotateX(-Math.PI / 4)
  return marker;
};
const addEscapeLine = () => {
  if (!T.scene.getObjectByName('escapeLine')) {
    const vertices = [
      new Vector3(10.709318501258421, 0.000003530609109464264, 19.48467859439551),
      new Vector3(10.523142535862755, 0.0962992344064097, 6.224359653191504),
      new Vector3(10.523142535862755, 0.1320867604684563, -8),
      // new Vector3(12.870857481827986, 0.05763676406343465, 9.556793694357296)
    ];
    curve = new CatmullRomCurve3(vertices);
    const tubeGeo = new TubeGeometry(curve, 80, 0.08);
    const mat = new MeshPhongMaterial({
      map: lineTexture,
      side: DoubleSide,
      transparent: true
    });
    const escapeLine = new Mesh(tubeGeo, mat);
    escapeLine.name = 'escapeLine';
    T.scene.add(escapeLine);
  } else {
    T.scene.getObjectByName('escapeLine').visible = true;
  }
};
const droneAni = () => {
  let time = Date.now();
  let t = (time % loopTime) / loopTime; // 计算当前时间进度百分比
  changePosition(t);
  changeLookAt(t);
}
// 获取曲线上特定位置 点向量
const changePosition = t => {
  const position = curve.getPointAt(t)  // t: 当前点在线条上的位置百分比，后面计算
  worker ? worker.position.copy(position) : ''
  // camera ? camera.position.set(position.x, position.y + 100, position.z - 100) : ''
};
// 获取曲线上特定点位的切线向量
const changeLookAt = t => {
  const position = curve.getPointAt(t)
  const tangent = curve.getTangentAt(t)
  const lookAtVec = tangent.add(position) // 位置向量和切线向量相加即为所需朝向的点向量
  // worker ? worker.lookAt(lookAtVec) : ''
  // camera ? camera.lookAt(lookAtVec) : ''
};
const createDOM = (htag, props) => { // 生成dom元素
  let tag = document.createElement(htag);
  for (let propName in props) {
    let propVal = props[propName];
    tag.setAttribute(propName, propVal);
  }
  return tag;
};
const restoreScene = () => {  // 场景还原
  let restore = new TWEEN.Tween(T.camera.position).to(new Vector3(...cameraPos), 3000)
    .easing(TWEEN.Easing.Cubic.InOut);
  restore.delay(100);
  restore.onUpdate(() => {
  }).onComplete(() => {
  });
  restore.start();
  let restore1 = new TWEEN.Tween(T.camera.rotation).to(new Vector3(...cameraRotation), 3000)
    .easing(TWEEN.Easing.Cubic.InOut);
  restore1.delay(100);
  restore1.onUpdate(() => {
  }).onComplete(() => {
  });
  restore1.start();
  let markers = document.querySelectorAll('.fire-marker');
  markers.forEach(marker => {
    marker.hidden = true;
  });
  T.scene.getObjectByName('escapeLine').visible = false;
};
const rayCasterSel = (e) => { // 射线选取
  let mouse = new Vector2();
  let raycaster = new Raycaster();
  mouse.x = ((e.clientX - container.value.getBoundingClientRect().left) / container.value.clientWidth) * 2 - 1
  mouse.y = -((e.clientY - container.value.getBoundingClientRect().top) / container.value.clientHeight) * 2 + 1
  raycaster.setFromCamera(mouse, T.camera);
  // console.log(T.scene)
  let intersects = raycaster.intersectObjects(T.scene.children, true);//[5].children
  if (intersects.length > 0) {
    console.log(intersects[0], '你点击到的点位是：')
    let intersected = intersects[0];
    if (intersected) {
      console.log(intersects.at(0).object, '选中的模型是：');
    }
    if (intersects.at(0).object.name === "vanguard_Mesh") {
      console.log('你点击到了工作人员');
      // const model = intersects.at(0).object;
      const model_g = T.scene.getObjectByName('worker');
      new TWEEN.Tween(T.camera.position).to(new Vector3(model_g.position.x + 3, model_g.position.y + 8, model_g.position.z + 5), 3000)
        .easing(TWEEN.Easing.Cubic.InOut).onUpdate(() => {
          T.camera.lookAt(model_g.position)
        }).delay(800).onComplete(() => {
          let marker = document.querySelector('.worker-marker');
          if (marker) {
            marker.hidden = false;
          } else {
            marker = addMarker({
              text: '我是化工材料配置员',
              pos: new Vector3(
                model_g.position.x,
                model_g.position.y + 3,
                model_g.position.z
              ),
              m_class: 'fire-marker worker-marker'
            });
            T.scene.add(marker);
          }
        }).start();

    }
  }
};
onUnmounted(() => { // 销毁
  clearTimeout()
  try {
    T.scene.clear()
    T.renderer.dispose()
    T.renderer.forceContextLoss()
    T.renderer.content = null
    const gl = T.renderer.domElement.getContext('webgl')
    gl && gl.getExtension('WEBGL_lose_context').loseContext()
  } catch (e) {
    console.log(e)
  }
})
</script>

<style>
.factory-main {
  width: 100%;
  height: 100vh;
}
.three {
  width: 100%;
  height: 100%;
}
.three .css3d {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  background: linear-gradient(
    90deg,
    #05142a 0%,
    rgba(93, 102, 116, 0.5) 25%,
    rgba(5, 20, 42, 0) 50%,
    rgba(93, 102, 116, 0.5) 75%,
    #05142a 100%
  );
}
.css3d .marker-body .fire-marker {
  padding: 10px 15px;
  font-size: 14px;
  font-family: HuXiaoBo-NanShen;
  border-radius: 2px;
  background-color: rgba(0, 127, 127, 0.6);
  color: rgb(0, 255, 234);
  box-shadow: 0px 0px 12px rgba(0, 255, 255, 0.5);
  border: 1px solid rgba(127, 255, 255, 0.25);
  text-align: center;
  cursor: default;
}
.tools {
  position: absolute;
  right: 3rem;
  top: 5rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 1rem;
}
</style>
